package com.sec.android.app.myfiles.module.cloud;

import android.app.FragmentManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Environment;
import android.util.SparseArray;

import com.samsung.android.sdk.slinkcloud.CloudGatewayFileBrowserUtils;
import com.samsung.android.sdk.slinkcloud.CloudGatewayFileTransferUtils;
import com.samsung.android.sdk.slinkcloud.CloudGatewayFileTransferUtils.TransferOptions;
import com.samsung.android.sdk.slinkcloud.CloudGatewayMediaSet;
import com.samsung.android.sdk.slinkcloud.CloudGatewayMediaStore;
import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.dialog.NameInUseDialogFragment;
import com.sec.android.app.myfiles.dialog.NameInUseDialogFragment.NameInUseDialogCallback;
import com.sec.android.app.myfiles.dialog.UnsupportedNameDialogFragment;
import com.sec.android.app.myfiles.dialog.UnsupportedNameDialogFragment.UnsupportedNameDialogCallback;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.info.FileType;
import com.sec.android.app.myfiles.listener.ConnectionChangeReceiver;
import com.sec.android.app.myfiles.listener.ConnectionChangeReceiver.OnConnectionBroadcast;
import com.sec.android.app.myfiles.listener.ListenerMgr;
import com.sec.android.app.myfiles.listener.SystemBroadcastReceiver;
import com.sec.android.app.myfiles.listener.SystemBroadcastReceiver.OnMediaUnmounted;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.module.abstraction.AbsFileOperationImp;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.abstraction.FileRecord.StorageType;
import com.sec.android.app.myfiles.module.local.file.LocalFileRecord;
import com.sec.android.app.myfiles.operation.FileOperationArgs;
import com.sec.android.app.myfiles.operation.FileOperationException;
import com.sec.android.app.myfiles.operation.FileOperator;
import com.sec.android.app.myfiles.operation.FileOperator.Operation;
import com.sec.android.app.myfiles.operation.ProgressListener;
import com.sec.android.app.myfiles.provider.PrivateMyFilesProvider;
import com.sec.android.app.myfiles.util.DrmUtils;
import com.sec.android.app.myfiles.util.SemFwWrapper;
import com.sec.android.app.myfiles.util.StorageMonitor;
import com.sec.android.app.myfiles.util.UiUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CloudFileOperationImp extends AbsFileOperationImp implements NameInUseDialogCallback, UnsupportedNameDialogCallback,
        OnConnectionBroadcast, OnMediaUnmounted, CloudGatewayFileBrowserUtils.QuotaError {
    // final static int MAX_ID_WAITING = 50000;
    // final int MAX_WAITING = 50000;
    static final int WAIT_INTERVAL = 300;
    private static final String TAG = "CloudFileOperationImp";
    private static final String mCloudCacheFolder = AppConstants.StoragePath.INTERNAL_ROOT + "/Android/data/com.samsung.android.slinkcloud/cache/filetransfer_cache";

    private FragmentManager mFragmentManager;
    private FileOperator mOperator;
    private boolean mIsSingleFile;
    private boolean mNeedScan;

    protected static boolean mUsbUnmounted;
    private String mLocalUsbPath;
    private int mDeleteDeviceId;

    public CloudFileOperationImp(Context context, ProgressListener listener) {
        super(context, listener);
    }

    @Override
    protected boolean isSupport(FileRecord record) {
        boolean bRet = false;

        if (record.getStorageType() == StorageType.Cloud)
            bRet = true;

        return bRet;
    }

    @Override
    protected boolean isSupportCopyMove(FileRecord src, FileRecord dst) {
        boolean bRet = false;

        if ((src.getStorageType() == StorageType.Cloud) || (dst.getStorageType() == StorageType.Cloud))
            bRet = true;

        return bRet;
    }

    private String mSessionId;
    private boolean mSessionComplete;
    private boolean mSessionSuccess;
    private String mStrToken;
    private String mCompleteList;
    private int mErrorType = CloudGatewayFileBrowserUtils.ERROR_NONE;

    private static final int INUSE = 1;
    private static final int STRANGE_CHARACTER = 2;

    private static final int DUMMY_STATUS = 1004;

    private final BroadcastReceiver mTransferSessionIdReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            mSessionId = intent.getStringExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_ID);
            int percent = intent.getIntExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_STATUS_PERCENTAGE, 0);

            int sentFileNum = intent.getIntExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_STATUS_SENTFILENUM, 0);
            int fileNum = intent.getIntExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_STATUS_FILENUM, 1);
            mCompleteList = intent.getStringExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_STATUS_FILELIST);
            mErrorType = intent.getIntExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_STATUS_ERROR_TYPE, CloudGatewayFileBrowserUtils.ERROR_NONE);

            int status = intent.getIntExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_STATUS_CODE, DUMMY_STATUS);
            Log.d(this, "Session status : " + status + "-errorType = " + mErrorType);

            switch (status) {
                case CloudGatewayFileTransferUtils.SESSION_STATUS_ERROR:
                    /* FALL THROUGH */
                case CloudGatewayFileTransferUtils.SESSION_STATUS_CANCELED:
                    synchronized (CloudFileOperationImp.this) {
                        mSessionComplete = true;
                    }
                    mSessionSuccess = false;
                    break;
                case CloudGatewayFileTransferUtils.SESSION_STATUS_PREPARE:
                    updateProgress(AppConstants.INDETERMINATE_PROGRESS_START, fileNum, sentFileNum);
                    break;
                case CloudGatewayFileTransferUtils.SESSION_STATUS_STARTED:
                    updateProgress(AppConstants.INDETERMINATE_PROGRESS_END, fileNum, sentFileNum);
                    break;
                default:
                    updateProgress(percent, fileNum, sentFileNum);
                    int len = (mCompleteList == null) ? 0 : mCompleteList.length();
                    Log.d(this, "mSessionId : " + mSessionId + ", nSentFileNum : " + sentFileNum + ", nFileNum : " + fileNum + ", percent : " + percent + " mCompleteList : " + (mCompleteList == null) + " len : " + len);

                    if (percent == 100) {
                        synchronized (CloudFileOperationImp.this) {
                            mSessionComplete = true;
                        }
                        mSessionSuccess = true;
                    }
                    break;
            }
        }

        private void updateProgress(int progress, int fileNum, int sentFileNum) {
            mProgresslistener.onProgressChanged(null, progress, 100);
            mOperator.setProgressMax(FileOperator.COUNT_PROGRESS, fileNum);
            mOperator.setCurProgress(FileOperator.COUNT_PROGRESS, sentFileNum);
        }
    };

    private final BroadcastReceiver mTransferRenameReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mSessionId = intent.getStringExtra(CloudGatewayFileTransferUtils.EXTRA_SESSION_ID);
            mStrToken = intent.getStringExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_TOKEN);

            String strNewName = intent.getStringExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_NEWNAME);
            String strOldName = intent.getStringExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_OLDNAME);

            boolean isDirectory = intent.getBooleanExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_ISDIRECTORY, false);
            int nReqCode = intent.getIntExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_STATUS, 0);

            Log.d(this, "check newName : " + strNewName + " oldName : " + strOldName);

            switch (nReqCode) {
                case INUSE:
                    showRenamePopup(strOldName, isDirectory);
                    break;
                case STRANGE_CHARACTER:
                    showUnsupportedPopup(strOldName, isDirectory);
                    break;
            }
            Log.d(this, "mSessionId = " + mSessionId);
        }
    };

    private void showRenamePopup(String name, boolean isDirectory) {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_START, false);

        FileRecord dst = new CloudFileRecord(name);
        if (isDirectory) {
            dst.setFileType(FileType.FOLDER);
        }
        NameInUseDialogFragment dialog = NameInUseDialogFragment.getInstance(dst, this);
        dialog.showAllowingStateLoss(mFragmentManager, "NameInUse");
    }

    private void showUnsupportedPopup(String name, boolean isDirectory) {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_START, false);

        FileRecord dst = new CloudFileRecord(name);
        if (isDirectory) {
            dst.setFileType(FileType.FOLDER);
        }

        UnsupportedNameDialogFragment unsupportedDialog = UnsupportedNameDialogFragment.getInstance(dst, this, mIsSingleFile);
        unsupportedDialog.showAllowingStateLoss(mFragmentManager, "UnsupportedName");
    }

    private void registerReceiver() {
        try {
            mContext.registerReceiver(mTransferSessionIdReceiver, new IntentFilter(
                    CloudGatewayFileTransferUtils.FILE_TRANSFER_SESSION_ID_BROADCAST_ACTION));
            mContext.registerReceiver(mTransferRenameReceiver, new IntentFilter(CloudGatewayFileTransferUtils.ACTION_RENAME_REQ));
        } catch (NullPointerException e) {
            Log.e(this, "NullPointerException:" + e.toString());
        }
    }

    private void unregisterReceiver() {
        try {
            mContext.unregisterReceiver(mTransferRenameReceiver);
            mContext.unregisterReceiver(mTransferSessionIdReceiver);
        } catch (IllegalArgumentException e) {
            Log.e(this, "IllegalArgumentException:" + e.toString());
        }
    }

    private synchronized boolean waitCloudOperation() {
        mSessionComplete = false;

        boolean bRet = true;
        try {
            while (!mSessionComplete && !mCancel && !mUsbUnmounted && (mErrorType == CloudGatewayFileBrowserUtils.ERROR_NONE)) {
                wait(WAIT_INTERVAL);
            }

            if (mCancel || mUsbUnmounted) {
                Log.d(this, "Progress sessionInfo canceled " + mSessionId);
                if (mSessionId != null) {
                    boolean cancelResult = CloudGatewayFileTransferUtils.getInstance(mContext).cancelFileTransferWithResult(mSessionId);
                    Log.d(this, "cancel Result : " + cancelResult);
                    if (!cancelResult) {
                        mSessionSuccess = true;
                        mCancel = false;
                    }
                }
            }

            if (mUsbUnmounted) {
                bRet = false;
                mUsbUnmounted = false;
            } else {
                bRet = mCancel ? false : mSessionSuccess;
            }
        } catch (InterruptedException e) {
            Log.e(this, "InterruptedException:" + e.toString());
        }

        return bRet;
    }

    private static final int LocalDeviceId = 1;

    private boolean freeSpaceCheck(ArrayList<FileRecord> src, FileRecord dst) {
        boolean bRet = true;
        long fileSize = 0;

        for (FileRecord file : src) {
            fileSize += file.getSize();
        }

        if (StorageMonitor.isInternalPath(dst.getFullPath())) {
            bRet = fileSize < StorageMonitor.getDeviceStorageFreeSpace();
        } else if (StorageMonitor.isMountedSdCardPath(dst.getFullPath())) {
            bRet = fileSize < StorageMonitor.getStorageFreeSpace(mContext, StorageMonitor.iStorageType.EXTERNAL_SD);
        }

        return bRet;
    }

    private boolean moveCopy(ArrayList<FileRecord> src, FileRecord dst, boolean isMove, boolean dupCheck, ArrayList<FileRecord> handledSrcList) throws FileOperationException {
        int size = (src != null) ? src.size() : 0;
        if (size == 0) {
            return false;
        }

        boolean isSrcCloud = (src.get(0).getStorageType() == StorageType.Cloud);
        boolean isDstCloud = (dst.getStorageType() == StorageType.Cloud);

        boolean isDownLoad = isSrcCloud && !isDstCloud;
        boolean isUpLoad = !isSrcCloud && isDstCloud;

        if (isDownLoad && !freeSpaceCheck(src, dst)) {
            if (StorageMonitor.isRemovedExtSDCard(dst.getFullPath())) {
                throw new FileOperationException(mContext.getResources().getString(R.string.failed_copy_items));
            } else {
                throw new FileOperationException(mContext.getResources().getString(R.string.not_enough_memory_popup));
            }
        }

        ListenerMgr listenerMgr = new ListenerMgr();
        listenerMgr.addListener(new ConnectionChangeReceiver(mContext, this, ListenerMgr.LifeCycle.CREATE, ListenerMgr.LifeCycle.DESTROY));
        listenerMgr.addListener(new SystemBroadcastReceiver(mContext, this, ListenerMgr.LifeCycle.CREATE, ListenerMgr.LifeCycle.DESTROY));

        registerReceiver();
        listenerMgr.notifyCreate();

        int srcDeviceId;
        int dstDeviceId;

        String srcParentId;
        String dstParentId;

        String[] selectedFile = new String[size];
        CloudGatewayMediaSet mediaSet;
        int index = 0;
        mLocalUsbPath = null;

        if (isDownLoad) { // download
            srcDeviceId = ((CloudFileRecord) src.get(0)).getDeviceId();
            srcParentId = ((CloudFileRecord) src.get(0)).getParentId();
            mNeedScan = true;

            dstDeviceId = LocalDeviceId;
            dstParentId = dst.getPath();

            if (StorageMonitor.startWithUsbRoot(dst.getFullPath()))
                mLocalUsbPath = dst.getFullPath();

            for (FileRecord rec : src) {
                if (rec != null) {
                    selectedFile[index++] = ((CloudFileRecord) rec).getFileId();
                }
            }
            mediaSet = CloudGatewayMediaSet.createFromFileBrowserIds(srcDeviceId, srcParentId, selectedFile, true,
                    CloudGatewayMediaStore.FileBrowser.FileBrowserColumns.LAST_MODIFIED + " DESC");
        } else if (isUpLoad) { // upload
            dstDeviceId = ((CloudFileRecord) dst).getDeviceId();
            dstParentId = ((CloudFileRecord) dst).getParentId();
            mNeedScan = isMove;

            if (StorageMonitor.startWithUsbRoot(src.get(0).getFullPath()))
                mLocalUsbPath = src.get(0).getFullPath();

            for (FileRecord rec : src) {
                if (rec != null) {
                    selectedFile[index++] = rec.getFullPath();
                }
            }
            mediaSet = CloudGatewayMediaSet.createFromLocalFilePaths(selectedFile);
        } else /*if (isSrcCloud && isDstCloud)*/ { // transfer b/w clouds
            srcDeviceId = ((CloudFileRecord) src.get(0)).getDeviceId();
            srcParentId = ((CloudFileRecord) src.get(0)).getParentId();

            dstDeviceId = ((CloudFileRecord) dst).getDeviceId();
            dstParentId = ((CloudFileRecord) dst).getParentId();

            for (FileRecord rec : src) {
                if (rec != null) {
                    selectedFile[index++] = ((CloudFileRecord) rec).getFileId();
                }
            }

            mediaSet = CloudGatewayMediaSet.createFromFileBrowserIds(srcDeviceId, srcParentId, selectedFile, true,
                    CloudGatewayMediaStore.FileBrowser.FileBrowserColumns.LAST_MODIFIED + " DESC");
        }
        String strSessionId = CloudGatewayFileTransferUtils.getInstance(mContext).createPremadeUniqueSessionID("MYFILES", dstDeviceId);

        TransferOptions options = new TransferOptions();
        options.transferImmediately = false;
        options.useTrackingSession = true;
        options.waitForRename = dupCheck;
        options.skipIfDuplicate = false;
        options.targetDirectoryPath = dstParentId;
        options.deleteSourceFilesWhenTransferIsComplete = isMove;

        boolean bRet;
        try {
            CloudGatewayFileTransferUtils.getInstance(mContext).transferFiles(mediaSet, dstDeviceId, options, strSessionId);
            bRet = waitCloudOperation();
        } catch (Exception e) {
            Log.e(TAG, "moveCopy fail - " + e.getMessage());
            bRet = false;
        }

        if (!bRet && isDownLoad) {
            deleteCloudCacheFiles();
        }

        if ((handledSrcList != null) && (mCompleteList != null)) {
            StringTokenizer st = new StringTokenizer(mCompleteList, ";:;:");
            while (st.hasMoreElements()) {
                handledSrcList.add(new LocalFileRecord(st.nextToken()));
            }
        }

        mLocalUsbPath = null;
        unregisterReceiver();
        listenerMgr.notifyDestroy();

        if (!bRet && !isDownLoad && mErrorType != CloudGatewayFileBrowserUtils.ERROR_NONE) {
            throw new FileOperationException(getQuotaErrorMsg(mContext, dst, mErrorType), false);
        }

        return bRet;
    }

    private void deleteCloudCacheFiles() {
        File file = SemFwWrapper.file(mCloudCacheFolder);

        if (file.exists()) {
            File[] childFileList = file.listFiles();

            if (childFileList != null) {
                for (File childFile : childFileList) {
                    if (childFile.isFile()) {
                        if (!childFile.delete())
                            Log.d(this, "deleteCloudCacheFiles : " + childFile.getAbsolutePath());
                    }
                }
            }
        }
    }

    @Override
    protected boolean _copy(FileRecord src, FileRecord dst) throws FileOperationException {
        return false;
    }

    @Override
    protected boolean _move(FileRecord src, FileRecord dst) throws FileOperationException {
        return false;
    }

    @Override
    protected boolean _copy(ArrayList<FileRecord> src, FileRecord dst, FragmentManager fragmentManager, boolean singleFile, boolean dupCheck,
                            FileOperator operator) throws FileOperationException {
        mFragmentManager = fragmentManager;
        mIsSingleFile = singleFile;
        mOperator = operator;

        if (src.get(0).isChildDirectory(dst)) {
            throw new FileOperationException(mContext.getResources().getString(R.string.destination_folder_is_subfolder_of_source_folder));
        }

        if (dst.getStorageType() == StorageType.Cloud) {
            int countDrm = countDrmFilesInSource(mContext, src);
            if (countDrm > 0 && ((CloudFileRecord) dst).getCloudType() == FileRecord.CloudType.SamsungDrive) {
                String message = UiUtils.getDrmWarningMessage(mContext, Operation.COPY, countDrm);
                if (message != null) {
                    throw new FileOperationException(message);
                }
            }
        }

        return moveCopy(src, dst, false, dupCheck, null);
    }

    @Override
    protected boolean _move(ArrayList<FileRecord> src, FileRecord dst, FragmentManager fragmentManager, boolean singleFile, FileOperator operator, ArrayList<FileRecord> handledSrcList)
            throws FileOperationException {
        mFragmentManager = fragmentManager;
        mIsSingleFile = singleFile;
        mOperator = operator;

        if (src.get(0).getPath().equals(dst.getPath())) {
            throw new FileOperationException(mContext.getResources().getString(R.string.destination_folder_is_same_as_source_folder));
        }
        if (src.get(0).isChildDirectory(dst)) {
            throw new FileOperationException(mContext.getResources().getString(R.string.destination_folder_is_subfolder_of_source_folder));
        }

        if (dst.getStorageType() == StorageType.Cloud) {
            int countDrm = countDrmFilesInSource(mContext, src);
            if (countDrm > 0 && ((CloudFileRecord) dst).getCloudType() == FileRecord.CloudType.SamsungDrive) {
                String message = UiUtils.getDrmWarningMessage(mContext, Operation.MOVE, countDrm);
                if (message != null) {
                    throw new FileOperationException(message);
                }
            }
        }

        return moveCopy(src, dst, true, true, handledSrcList);
    }

    @Override
    protected boolean _rename(FileRecord src, FileRecord dst) throws FileOperationException {
        boolean bRet = false;

        if (src.getStorageType() == StorageType.Cloud) {
            bRet = CloudGatewayFileBrowserUtils.getInstance(mContext).mrrControlCommand(((CloudFileRecord) src).getDeviceId(),
                    CloudGatewayFileBrowserUtils.REMOTE_FOLDER_RENAME, ((CloudFileRecord) src).getParentId(),
                    ((CloudFileRecord) src).getFileId(), dst.getName(), null);
        }
        return bRet;
    }

    @Override
    protected boolean _delete(FileRecord record) throws FileOperationException {
        int deviceId = ((CloudFileRecord) record).getDeviceId();
        String fileId = ((CloudFileRecord) record).getFileId();

        Log.d(this, "_delete : " + deviceId + " " + fileId);
        mProgresslistener.onProgressChanged(null, 0, 100);
        boolean ret = CloudGatewayFileBrowserUtils.getInstance(mContext).mrrControlCommand(deviceId, CloudGatewayFileBrowserUtils.REMOTE_FOLDER_RMDIR,
                null, fileId, null, null);

        if (ret) {
            mProgresslistener.onProgressChanged(null, 100, 100);
        }
        return ret;
    }

    @Override
    protected boolean _delete(ArrayList<FileRecord> recordList, FileOperator operator) throws FileOperationException {
        boolean bRet = false;
        mOperator = operator;
        if (recordList != null) {
            if (!recordList.isEmpty()) {
                SparseArray<List<String>> targetMap = new SparseArray<>();
                List<String> selectedFiles;
                int deviceId;
                for (FileRecord record : recordList) {
                    if (record instanceof CloudFileRecord) {
                        deviceId = ((CloudFileRecord) record).getDeviceId();
                        selectedFiles = targetMap.get(deviceId);
                        if (selectedFiles == null) {
                            selectedFiles = new ArrayList<>();
                            targetMap.put(deviceId, selectedFiles);
                        }
                        selectedFiles.add(((CloudFileRecord) record).getFileId());
                    }
                }
                int targetGroupSize = targetMap.size();
                for (int i = 0; i < targetGroupSize && !mCancel; i++) {
                    mDeleteDeviceId = targetMap.keyAt(i);
                    selectedFiles = targetMap.get(mDeleteDeviceId);
                    if (selectedFiles != null) {
                        bRet = CloudGatewayFileBrowserUtils.getInstance(mContext).mrrControlBatchCommand(mDeleteDeviceId, CloudGatewayFileBrowserUtils.REMOTE_FOLDER_RMDIR_BATCH, selectedFiles.toArray(new String[selectedFiles.size()]), null);
                    }

                    if (!bRet) {
                        break;
                    }
                }
                mProgresslistener.onProgressChanged(null, 0, 100);
            }
        }

        if (bRet) {
            mProgresslistener.onProgressChanged(null, 100, 100);
        }

        return bRet;
    }

    @Override
    protected FileRecord _mkdir(FileRecord dst, String name) throws FileOperationException {
        CloudFileRecord ret = (CloudFileRecord) dst;

        String fileId = CloudGatewayFileBrowserUtils.getInstance(mContext).mrrControlCommandWithStringReturn(ret.getDeviceId(),
                CloudGatewayFileBrowserUtils.REMOTE_FOLDER_MKDIR, ret.getFileId(), null, name, this);
        Log.d(this, "mkdir : " + ret.getName() + " " + fileId);

        if (fileId == null) {
            return null;
        }

        if (mErrorType != CloudGatewayFileBrowserUtils.ERROR_NONE) {
            throw new FileOperationException(getQuotaErrorMsg(mContext, ret, mErrorType), false);
        }

        ret = new CloudFileRecord(ret.getDeviceId(), ret.getFileId(), fileId, name);
        return ret;
    }

    @Override
    protected FileRecord _getUniqueFileRecord(FileRecord record) {
        int i = 1;
        String nameWithoutExt = record.getNameWithoutExt();
        String ext = record.getExtForRename();
        String filename = record.getName();

        StringBuilder sb;

        while (record.exists(mContext)) {
            sb = new StringBuilder();
            sb.append(nameWithoutExt);
            sb.append(" (");
            sb.append(i++);
            sb.append(')');

            if (ext != null && !ext.isEmpty()) {
                sb.append('.');
                sb.append(ext);
            }

            filename = sb.toString();
        }

        String fullPath = record.getPath() + "/" + filename;
        return FileRecord.createFileRecord(StorageType.Cloud, fullPath);
    }

    @Override
    protected ArrayList<FileRecord> _getListInDirectory(FileRecord record) {
        //Do nothing in CloudFileOperation
        return null;
    }

    @Override
    protected void _preOperation(Operation operationType) {
        //Do nothing in CloudFileOperation
    }

    @Override
    protected void _postOperation(Operation operationType, FileOperationArgs args, int handledCount) {
        if (mNeedScan) {
            mNeedScan = false;
            scanForOperation(operationType, args, handledCount);
            PrivateMyFilesProvider.clearCache(mContext, null);
        }
    }

    @Override
    public void onUnsupportedFileCancel() {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_CANCEL, false);
        CloudGatewayFileTransferUtils.getInstance(mContext).cancelFileTransferBySessionId(mSessionId);
    }

    @Override
    public void onUnsupportedFileSkip() {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_SKIP, false);
    }

    @Override
    public void onUnsupportedFileRename(boolean applyAllItems) {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_RENAME, applyAllItems);
    }

    @Override
    public void onDuplicateFileSkip(boolean applyAllItems) {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_SKIP, applyAllItems);

        if (applyAllItems) {
            mProgresslistener.onProgressChanged(null, 100, 100);

            synchronized (CloudFileOperationImp.this) {
                try {
                    wait(WAIT_INTERVAL);// to show progress
                } catch (InterruptedException e) {
                    Log.e(this, "InterruptedException:" + e.toString());
                }
                mSessionComplete = true;
            }
            mSessionSuccess = false;
        }
    }

    @Override
    public void onDuplicateFileReplace(boolean applyAllItems) {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_REPLACE, applyAllItems);
    }

    @Override
    public void onDuplicateFileRename(boolean applyAllItems) {
        sendRenameBroadcast(mContext, mStrToken, CloudGatewayFileTransferUtils.RENAME_STATUS_RSP_RENAME, applyAllItems);
    }

    private static void sendRenameBroadcast(Context context, String strToken, int nStatusCode, boolean applyAllItems) {
        try {
            Intent intent = new Intent(CloudGatewayFileTransferUtils.ACTION_RENAME_RSP);
            intent.putExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_TOKEN, strToken);
            intent.putExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_NEWNAME, "");
            intent.putExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_STATUS, nStatusCode);
            intent.putExtra(CloudGatewayFileTransferUtils.EXTRA_RENAME_APPLYALL, applyAllItems);
            context.sendBroadcast(intent);
        } catch (Exception e) {
            Log.e(TAG, "Exception:" + e.toString());
        }
    }

    @Override
    protected boolean isSupportRecursiveCopyMove() {
        return false;
    }

    @Override
    public void onConnect() {
    }

    @Override
    public synchronized void onDisconnect() {
        mCancel = true;
    }

    @Override
    public void onMediaUnmounted(String path) {
        if ((mLocalUsbPath != null) && (mLocalUsbPath.startsWith(path))) {
            mUsbUnmounted = true;
        }
    }

    @Override
    protected void makeScanLists(Operation op, ArrayList<String> pathList, ArrayList<String> fileList, FileOperationArgs args, int handledCount) {
        FileRecord srcRecord;
        FileRecord dstRecord = args.mDst;
        switch (op) {
            case COPY:
                final String androidDataPath = Environment.getExternalStorageDirectory() + "/Android/data/";
                String fullPath = dstRecord.getFullPath();
                if (!fullPath.startsWith(androidDataPath)) {
                    pathList.add(fullPath);
                }
                break;
            case MOVE:
                srcRecord = args.mSrcList.get(0);
                if (srcRecord.getStorageType().equals(StorageType.Cloud)) {
                    pathList.add(dstRecord.getFullPath());
                } else {
                    addRootPath(pathList, args.mSrcList, null);
                }
                break;
        }
    }

    public static int countDrmFilesInSource(Context context, ArrayList<FileRecord> fileRecords) {
        int nCount = 0;

        if (fileRecords != null && !fileRecords.isEmpty()) {
            if (fileRecords.get(0).getStorageType() != StorageType.Cloud) {
                final int threadCount = Math.min(AppConstants.NUMBER_OF_THREADS, fileRecords.size());
                ExecutorService fileCheckExecutor = Executors.newFixedThreadPool(threadCount);
                CompletionService<Boolean> completionService = new ExecutorCompletionService<>(fileCheckExecutor);

                for (FileRecord record : fileRecords) {
                    completionService.submit(new DrmChecker(context, record.getFullPath()));
                }

                int size = fileRecords.size();
                for (int i = 0; i < size; i++) {
                    try {
                        if ((completionService.take().get()).booleanValue()) {
                            nCount++;
                            if (nCount > 1) {
                                break;
                            }
                        }
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }

                fileCheckExecutor.shutdown();
            }
        }

        return nCount;
    }

    @Override
    public void onQuotaError(int errType, int nCmd) {
        mErrorType = errType;
    }

    public static int getResIdQuotaError(int errType) {
        int resId = -1;
        switch (errType) {
            case CloudGatewayFileBrowserUtils.ERROR_REACH_MAX_ITEM:
                resId = R.string.msg_reach_max_items;
                break;
            case CloudGatewayFileBrowserUtils.ERROR_OUT_OF_STORAGE:
                resId = R.string.msg_not_enough_space_cloud;
                break;
            default:
                break;
        }

        return resId;
    }

    public static String getQuotaErrorMsg(Context context, FileRecord dst, int errType) {
        String ret = null;
        String storage = null;
        boolean isDestCloud = (dst.getStorageType() == StorageType.Cloud);
        if (isDestCloud) {
            storage = dst.getDisplayName(context);
        }

        int msgErrId = getResIdQuotaError(errType);

        if (msgErrId != -1) {
            ret = context.getResources().getString(msgErrId);
            ret = (storage != null) ? String.format(ret, storage) : ret;
        }
        return ret;
    }

    @Override
    protected void _cancel() {
        if (mOperator != null && Operation.DELETE == mOperator.getOperation()) {
            CloudGatewayFileBrowserUtils.getInstance(mContext).mrrControlBatchCommand(mDeleteDeviceId, CloudGatewayFileBrowserUtils.REMOTE_FOLDER_RMDIR_BATCH_CANCEL, null, null);
        }
    }

    private static class DrmChecker implements Callable<Boolean> {
        private String mPath;
        private Context mContext;

        DrmChecker(Context context, String path) {
            mContext = context;
            mPath = path;
        }

        @Override
        public Boolean call() throws Exception {
            return DrmUtils.isDRMFile(mContext, mPath);
        }
    }
}
